Flutterのcontextの「お気持ち」を理解する #Flutter

您所在的位置:网站首页 flutter build context Flutterのcontextの「お気持ち」を理解する #Flutter

Flutterのcontextの「お気持ち」を理解する #Flutter

2024-01-13 04:13| 来源: 网络整理| 查看: 265

ここでいう「お気持ち」というのは、「定義」とか「正体」とかではなく、

「何のためにあるのか」とか「どう使うことが想定されているのか」とか

そういうことを意味します。「作った人がどういうつもりだったか」という意味で「お気持ち」と言っています。

私がFlutterを始めてすぐの頃、contextとかBuildContextなるものが何なのか、どう使うのか、さっぱり理解できなくて困ったので、その辺を助ける記事です。

「実体はなんなのか」とか「正体はなんなのか」といった説明はしませんのでご了承ください。

一番言いたい結論は

Hoge.of(context) で「引数に与えた文脈におけるHoge」

を意味するということです。

BuildContext型のcontext変数

contextとサラッと言ってますが、より具体的にはBuildContextというクラスですね。

BuildContextクラスのドキュメント

こちらをよく読むと実は「お気持ち」も書いてあるのですが、簡潔に終わってますので、この記事で改めて説明します。

"context"は「文脈」という意味

そもそも何で"context"と呼ばれているのか、を考えてみることにしましょう。そこには命名者の意図があるはずです。

"context"という英単語は「文脈」という意味です。

「この文脈における『適当』は『雑』ではなく『ちょうどいい』という意味です。」

「この文脈における『押すなよ!』というのは『押せ!』という意味です。」

「この文脈においては、お茶漬けを勧めているのではなく『帰れ』と言っています。」

のように使いますね。

「文脈」は「環境」や「状況」と言い換えることもできます。

「この状況における『適当』は『雑』ではなく『ちょうどいい』という意味です。」

「この環境における『押すなよ!』というのは『押せ!』という意味です。」

「この状況においては、お茶漬けを勧めているのではなく『帰れ』と言っています。」

Hoge.of(context)で「この文脈におけるHoge」

Flutterの話に戻ります。

Flutterのcontextもやはり「文脈」「状況」「環境」を表します。

「今、どんな場所にいるのか」を表しているわけです。

もっと言うと、「Widgetツリー内のどこにいるか」を表しています。

Flutterのコードでcontextが出てくるのは大体次の2つの場合です。

builder of

一つずつ説明します。

builder builder:(BuildContext context){ return // (何かしらのWidget); }

builderは、Widget生成してWidgetツリー内にセットする時に呼ばれる関数です。

引数のcontextは、Flutterが自動的に与えます。我々はbuilder関数がcontextを受け取れるようにさえしておけば良いです。

自分でWidgetを作る場合は

@override Widget build(BuildContext context){ return // (何かしらのWidget); }

となりますが、BuildContextを受け取ってWidgetを返す関数という意味で同じです。

これらのコードに出てくるcontextは、受け取り口として用意されているものであって、使ってるわけじゃないですね。

では次。

of Hoge.of(context).// (何かしらのプロパティやメソッド);

こちらは、すでに持っているcontextという変数を実際に使っています。

Hoge.of(context)というのは、contextの場所からWidgetツリーを先祖に向かってさかのぼって、最初に見つかるHogeを返します。

実際にはHogeはThemeだったりDefaultTextStyleだったり自分で作った何かだったりします。

つまりHoge.of(context)は

「引数に与えたcontextはどのHogeの傘下なのか」

「引数に与えたcontextにはどんなHogeが設定されているか」

を調べているわけです。

Themeの例

実際のコードで見てみましょう。

次のようなWidgetAを作ります。

class WidgetA extends StatelessWidget { const WidgetA(this.text); final String text; @override Widget build(BuildContext context) { return Center( child: SizedBox( height: 100, width: 200, child: Card( color: Theme.of(context).primaryColor, child: Center(child: Text(text)), ), ), ); } }

レイアウト用のWidgetか挟まってて見にくいですが、重要なのはここだけです。

class WidgetA extends StatelessWidget { @override // contextはここで引数として与えられる Widget build(BuildContext context) { return Card( // 自分が置かれた文脈に設定されているThemeを調べている color: Theme.of(context).primaryColor, ), ); } }

Theme.of(context)の部分で、自分が置かれた文脈におけるThemeを探しに行き、自分自身の色として適用しています。

このWidgetAを、次のように配置してみましょう。

class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData(primaryColor: Colors.pink), home: Scaffold( appBar: AppBar(), body: Column( children: [ Theme( data: ThemeData(primaryColor: Colors.amber), child: const WidgetA('Fuga')), Column( children: [ const WidgetA('Foo'), Theme( data: ThemeData( primaryColor: Colors.teal, ), child: const WidgetA('Bar')) ], ) ], ), )); } }

ツリー図にするとこんな感じです。

Qiita用Widgetツリー図 context.png

それぞれのWidgetが何色のThemeの傘下になっているかによって色分けしてあります。

MaterialAppに色が設定してありますが、実はMaterialAppにはデフォルトのThemeを設定する機能がありまして、そこにピンク色を設定してあります。

これを

void main() => runApp(MyApp());

として実行すると次のようになります。

Screen Shot 2019-09-05 at 11.16.08.png

それぞれのWidgetが、自分の先祖にいるThemeを見つけてその色に染まっているのがわかると思います。

Fooと書いてあるWidgetAは、先祖にThemeがいないので、MaterialAppに設定されているピンク色になっていますね。

また、よく見ると最上段のAppBarもピンク色になっています。(時間や電波状況が表示されている部分)

これはScaffoldWidgetの一部なのですが、Scaffoldも、すぐ親にMaterialAppがいるのでピンク色になっています。

【注意】contextの位置が高すぎる場合

いちいちWidgetAなんて作らずに、単にズラズラとWidgetを配置していったらどうなるでしょうか?

つまりこういうことです。

レイアウト用のコードも挟まってて見にくいので、重要な部分だけ取り出したコードも下に載せておきます。

class MyApp extends StatelessWidget { @override // contextはここで与えられる Widget build(BuildContext context) { return MaterialApp( theme: ThemeData(primaryColor: Colors.pink), home: Scaffold( appBar: AppBar(), body: Column( children: [ Theme( data: ThemeData(primaryColor: Colors.blue), child: Center( child: SizedBox( height: 100, width: 200, child: Card( color: Theme.of(context).primaryColor, child: Center(child: Text('Fuga')), ), ), )), Column( children: [ Center( child: SizedBox( height: 100, width: 200, child: Card( color: Theme.of(context).primaryColor, child: Center(child: Text('Foo')), ), ), ), Theme( data: ThemeData( primaryColor: Colors.green, ), child: Center( child: SizedBox( height: 100, width: 200, child: Card( color: Theme.of(context).primaryColor, child: Center(child: Text('Bar')), ), ), ), ) ], ) ], ), )); } }

重要なところだけ版

class MyApp extends StatelessWidget { @override // contextはここで与えられる Widget build(BuildContext context) { return MaterialApp( theme: ThemeData(primaryColor: Colors.pink), home: Column( children: [ Theme( data: ThemeData(primaryColor: Colors.blue), child: Card( color: Theme.of(context).primaryColor, child: Center(child: Text('Fuga')), ), ), Column( children: [ child: Card( color: Theme.of(context).primaryColor, child: Center(child: Text('Foo')), ), Theme( data: ThemeData( primaryColor: Colors.green, ), child: Card( color: Theme.of(context).primaryColor, child: Center(child: Text('Bar')), // 閉じカッコ略 }

実行結果がこちら

Screen Shot 2019-09-05 at 11.29.59.png

ウィジェットツリー内のThemeとCardの位置関係はまったく変わっていないのに、Cardが自分の先祖にいるThemeにアクセスしてくれません!!

実はコードをよく見ると、引数contextが与えられているのはMyAppのbuild関数だけです。これはMaterialAppよりもさらに親です。

Theme.of(context)は与えられたcontextの地点からスタートしてツリーをさかのぼり、Themeを探しに行くことになりますが

そこ(MaterialAppよりも親)からスタートしてさかのぼっても、もうWidgetはありません。もちろんThemeもありません。

ということで、何も見つからないので、3つのCardはFlutterのデフォルトカラーである青色になってしまいました。

Hoge.of(context)でさかのぼりを開始するスタート地点は、そのコードを記述した位置ではなく、引数contextが与えられた位置からなので注意してください。

contextを途中で置き換える方法

このようにcontextの与えられる位置が高すぎる場合は、もっと下層でcontextを「発生」させなければなりません。

その方法を2つ紹介します。

自作Widgetを挟む

はじめのコードでやっていた方法です。WidgetAのような自作Widgetを用意すれば、自動的にその中にbuild関数があるはずですから、そこでcontextを受け取ることになります。

Builderを使う

実はFlutterにはそういう時に使えるWidgetが用意されています。

BuilderというWidgetがそれです。

Builder Class

これを挟み込むと、そこでcontextを受け取れます。

参考

ということで「お気持ち」の説明は以上です。

より詳しく知りたい方はこちらの記事がおすすめです。

FlutterのBuildContextとは何か

また、私の記事ですが、BuildContextと密接な関わりのあるInheritedWidgetについても書きましたのでぜひご覧ください。

InheritedWidgetの目的と使い方【Flutter】



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3